Unit BQueryUnit;

{$mode objfpc}{$H+}

Interface

Uses
  {$IFDEF TDS}
    TDSCTDataBase,
  {$ENDIF}
  {$IFDEF UIB}
    uib, uiblib,
  {$ENDIF}
  {$IFDEF IBX}
    IBDatabase, IBSQL,
  {$ENDIF}
  Classes, SysUtils, BSQLUnit, BCommonUnit;

Const
  STEP_MAX = 3;

  DB_FB = 1;
  DB_MS = 2;

Type

{ BQueryClass }

BQueryClass = Class
  Private
    bID, bKind: Integer;
    {$IFDEF UIB}
    bUIBQuery: TUIBQuery;
    bUIBDatabase: TUIBDataBase;
    bUIBWrite, bUIBRead: TUIBTransaction;
    {$ENDIF}
    {$IFDEF TDS}
    bTDSQuery: TTDSCTQuery;
    bTDSDatabase: TTDSCTDataBase;
    bTDSWrite, bTDSRead: TTDSCTTransaction;
    {$ENDIF}
    {$IFDEF IBX}
    bIBXQuery: TIBSQL;
    bIBXDatabase: TIBDataBase;
    bIBXWrite, bIBXRead: TIBTransaction;
    {$ENDIF}
    {$IFDEF UIB}
    Function GetUIB(Const aSQL: BSQLClass): Boolean;
    Function PostUIB(Const aSQL: BSQLClass): Boolean;
    {$ENDIF}
    {$IFDEF TDS}
    Function GetTDS(Const aSQL: BSQLClass): Boolean;
    Function PostTDS(Const aSQL: BSQLClass): Boolean;
    {$ENDIF}
    {$IFDEF IBX}
    Function GetIBX(Const aSQL: BSQLClass): Boolean;
    Function PostIBX(Const aSQL: BSQLClass): Boolean;
    {$ENDIF}
  Public
    Function ByString(Const aString: String): String;
    Function ByDouble(Const aString: String): Double;
    Function ByInteger(Const aString: String): Integer; Virtual;
    Function ByInt64(Const aString: String): Int64;
    Function ByBoolean(Const aString: String): Boolean;
    Function ByDate(Const aString: String): TDateTime;
    Function ByDateTime(Const aString: String): TDateTime;
    Function Get(Const aSQL: BSQLClass): Boolean; Virtual;
    Function Get(Const aString: String;
      Const aForExecute: Boolean = FALSE): Boolean; Virtual;
    Function Get(Const aMask: String; Const aParams: Array Of Const;
      Const aForExecute: Boolean = FALSE): Boolean; Virtual;
    Function Post(Const aSQL: BSQLClass): Boolean; Virtual;
    Function Post(Const aString: String;
      Const aForExecute: Boolean = FALSE): Boolean; Virtual;
    Function Post(Const aMask: String; Const aParams: Array Of Const;
      Const aForExecute: Boolean = FALSE): Boolean; Virtual;
    Function EOF: Boolean; Virtual;
    Procedure Go; Virtual;
    Procedure Next; Virtual;
    Procedure First; Virtual;
    Constructor Build(Const aID: Integer = 0);Virtual;
    Destructor Burn; Virtual;
End;

Type

{ BDBManagerClass }

BDBManagerClass = Class
  Strict Private
    bPools: TThreadList;
  Public
    Function AddDatabase(Const aKind: Integer; Const aServer, aBase, aUser,
      aPassword: String; Const aLib:String=''): Integer; Virtual;
    Procedure RemoveDatabase(Const aIndex: Integer); Virtual;
    Function GetKind(Const aIndex: Integer): Integer; Virtual;
    Procedure Connect(Const aIndex: Integer = 0;
      Const aPoolSize: Integer = 1); Virtual;
    {$IFDEF UIB}
    Procedure HoldUIBConnection(Const aIndex: Integer;
      Out aDatabase: TUIBDataBase; Out aRead, aWrite: TUIBTransaction);
    Procedure FreeUIBConnection(Const aIndex: Integer;
      Const aDatabase: TUIBDataBase);
    {$ENDIF}
    {$IFDEF TDS}
    Procedure HoldTDSConnection(Const aIndex: Integer;
      Out aDatabase: TTDSCTDataBase; Out aRead, aWrite: TTDSCTTransaction);
    Procedure FreeTDSConnection(Const aIndex: Integer;
      Const aDatabase: TTDSCTDataBase);
    {$ENDIF}
    {$IFDEF IBX}
    Procedure HoldIBXConnection(Const aIndex: Integer;
      Out aDatabase: TIBDataBase; Out aRead, aWrite: TIBTransaction);
    Procedure FreeIBXConnection(Const aIndex: Integer;
      Const aDatabase: TIBDataBase);
    {$ENDIF}
    Procedure Disconnect(Const aIndex: Integer = 0); Virtual;
    Constructor Build; Virtual;
    Destructor Burn; Virtual;
End;

Var
  DBManager: BDBManagerClass = nil;

Implementation

Const
  STR_MSSQL = '/usr/lib/libct.so';

Type

{ BConnectionClass }

BConnectionClass = Class
  Strict Private
    bBusy: Boolean;
    bKind: Integer;
    {$IFDEF UIB}
    bUIBDatabase: TUIBDataBase;
    bReadUIBTransaction: TUIBTransaction;
    bWriteUIBTransaction: TUIBTransaction;
    {$ENDIF}
    {$IFDEF TDS}
    bTDSDatabase: TTDSCTDataBase;
    bReadTDSTransaction: TTDSCTTransaction;
    bWriteTDSTransaction: TTDSCTTransaction;
    {$ENDIF}
    {$IFDEF IBX}
    bIBXDatabase: TIBDataBase;
    bReadIBXTransaction: TIBTransaction;
    bWriteIBXTransaction: TIBTransaction;
    {$ENDIF}
    Function GetConnected: Boolean;
  Public
    Property Kind: Integer Read bKind;
    Property Busy: Boolean Read bBusy;
    Property Connected: Boolean Read GetConnected;
    {$IFDEF UIB}
    Property UIBDatabase: TUIBDataBase Read bUIBDatabase;
    Procedure HoldUIBConnection(Out aDatabase: TUIBDataBase;
      Out aRead, aWrite: TUIBTransaction);
    Procedure FreeUIBConnection;
    {$ENDIF}
    {$IFDEF TDS}
    Property TDSDatabase: TTDSCTDataBase Read bTDSDatabase;
    Procedure HoldTDSConnection(Out aDatabase: TTDSCTDataBase;
      Out aRead, aWrite: TTDSCTTransaction);
    Procedure FreeTDSConnection;
    {$ENDIF}
    {$IFDEF IBX}
    Property IBXDatabase: TIBDataBase Read bIBXDatabase;
    Procedure HoldIBXConnection(Out aDatabase: TIBDataBase;
      Out aRead, aWrite: TIBTransaction);
    Procedure FreeIBXConnection;
    {$ENDIF}
    Constructor Build(Const aKind: Integer;
      Const aServer, aBase, aUser, aPassword, aLib: String);
    Destructor Burn;
End;

Type

{ BConnectionsPoolClass }

BConnectionsPoolClass = Class
  Strict Private
    bConnected: Boolean;
    bPool: TThreadList;
    bBase: String;
    bKind: Integer;
    bLib: String;
    bPassword: String;
    bServer: String;
    bUser: String;
    bPoolDepth: Integer;
    {$IFDEF UIB}
    bUIBDatabase: TUIBDataBase;
    bReadUIBTransaction: TUIBTransaction;
    bWriteUIBTransaction: TUIBTransaction;
    {$ENDIF}
    {$IFDEF TDS}
    bTDSDatabase: TTDSCTDataBase;
    bReadTDSTransaction: TTDSCTTransaction;
    bWriteTDSTransaction: TTDSCTTransaction;
    {$ENDIF}
    {$IFDEF IBX}
    bIBXDatabase: TIBDataBase;
    bReadIBXTransaction: TIBTransaction;
    bWriteIBXTransaction: TIBTransaction;
    {$ENDIF}
  Public
    Property Kind: Integer Read bKind;
    Property Server: String Read bServer;
    Property Base: String Read bBase;
    Property User: String Read bUser;
    Property Password: String Read bPassword;
    Property Lib: String Read bLib;
    Property Connected: Boolean Read bConnected;
    Property PoolDepth: Integer Read bPoolDepth;
    {$IFDEF UIB}
    Property ReadUIBTransaction: TUIBTransaction Read bReadUIBTransaction;
    Property WriteUIBTransaction: TUIBTransaction Read bWriteUIBTransaction;
    Property UIBDatabase: TUIBDataBase Read bUIBDatabase;
    Procedure HoldUIBConnection(Out aDatabase: TUIBDataBase;
      Out aRead, aWrite: TUIBTransaction);
    Procedure FreeUIBConnection(Const aDatabase: TUIBDataBase);
    {$ENDIF}
    {$IFDEF TDS}
    Property ReadTDSTransaction: TTDSCTTransaction Read bReadTDSTransaction;
    Property WriteTDSTransaction: TTDSCTTransaction Read bWriteTDSTransaction;
    Property TDSDatabase: TTDSCTDataBase Read bTDSDatabase;
    Procedure HoldTDSConnection(Out aDatabase: TTDSCTDataBase;
      Out aRead, aWrite: TTDSCTTransaction);
    Procedure FreeTDSConnection(Const aDatabase: TTDSCTDataBase);
    {$ENDIF}
    {$IFDEF IBX}
    Property ReadIBXTransaction: TIBTransaction Read bReadIBXTransaction;
    Property WriteIBXTransaction: TIBTransaction Read bWriteIBXTransaction;
    Property IBXDatabase: TIBDataBase Read bIBXDatabase;
    Procedure HoldIBXConnection(Out aDatabase: TIBDataBase;
      Out aRead, aWrite: TIBTransaction);
    Procedure FreeIBXConnection(Const aDatabase: TIBDataBase);
    {$ENDIF}
    Procedure Connect(Const aPoolDepth: Integer);
    Procedure Disconnect;
    Constructor Build(Const aKind: Integer;
      Const aServer, aBase, aUser, aPassword, aLib: String);
    Destructor Burn;
End;

{ BConnectionClass }

Function BConnectionClass.GetConnected: Boolean;
Begin
  Result := FALSE;
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}
      Result := Assigned(bUIBDatabase) And bUIBDatabase.Connected
      {$ENDIF}
      {$IFDEF IBX}
      Result := Assigned(bIBXDatabase) And bIBXDatabase.Connected
      {$ENDIF};
    DB_MS:
      {$IFDEF TDS}
      Result := Assigned(bTDSDatabase) And bTDSDatabase.Connected
      {$ENDIF};
  End;
End;

{$IFDEF IBX}
Procedure BConnectionClass.HoldIBXConnection(Out aDatabase: TIBDataBase; Out
  aRead, aWrite: TIBTransaction);
Begin
  aDatabase := bIBXDatabase;
  aRead := bReadIBXTransaction;
  aWrite := bWriteIBXTransaction;
  bBusy := TRUE;
End;

Procedure BConnectionClass.FreeIBXConnection;
Begin
  If bReadIBXTransaction.InTransaction Then bReadIBXTransaction.Commit;
  If bWriteIBXTransaction.InTransaction Then bWriteIBXTransaction.RollBack;
  bBusy := FALSE;
End;
{$ENDIF}

{$IFDEF UIB}
Procedure BConnectionClass.HoldUIBConnection(Out aDatabase: TUIBDataBase;
  Out aRead, aWrite: TUIBTransaction);
Begin
  aDatabase := bUIBDatabase;
  aRead := bReadUIBTransaction;
  aWrite := bWriteUIBTransaction;
  bBusy := TRUE;
End;

Procedure BConnectionClass.FreeUIBConnection;
Begin
  If bReadUIBTransaction.InTransaction Then bReadUIBTransaction.Commit;
  If bWriteUIBTransaction.InTransaction Then bWriteUIBTransaction.RollBack;
  bBusy := FALSE;
End;

{$ENDIF}

{$IFDEF TDS}
Procedure BConnectionClass.HoldTDSConnection(Out aDatabase: TTDSCTDataBase;
  Out aRead, aWrite: TTDSCTTransaction);
Begin
  aDatabase := bTDSDatabase;
  aRead := bReadTDSTransaction;
  aWrite := bWriteTDSTransaction;
  bBusy := TRUE;
End;

Procedure BConnectionClass.FreeTDSConnection;
Begin
  If bReadTDSTransaction.Active Then bReadTDSTransaction.Commit;
  If bWriteTDSTransaction.Active Then bWriteTDSTransaction.Rollback;
  bBusy := FALSE;
End;

{$ENDIF}

Constructor BConnectionClass.Build(Const aKind: Integer; Const aServer, aBase,
  aUser, aPassword, aLib: String);
Begin
  bKind := aKind;
  Case Kind Of
    DB_FB:
      Begin
        {$IFDEF UIB}
        bUIBDatabase := TUIBDataBase.Create(nil);
        bUIBDatabase.DatabaseName := Format('%s:%s', [aServer, aBase]);
        bUIBDatabase.UserName := aUser;
        bUIBDatabase.PassWord := aPassword;
        bUIBDatabase.CharacterSet := csUTF8;
        If Not(aLib = '') Then bUIBDatabase.LibraryName := aLib;

        bReadUIBTransaction := TUIBTransaction.Create(nil);
        bReadUIBTransaction.DataBase := bUIBDatabase;
        bReadUIBTransaction.Options :=
          [tpRead, tpReadCommitted, tpWait, tpRecVersion];

        bWriteUIBTransaction := TUIBTransaction.Create(nil);
        bWriteUIBTransaction.DataBase := bUIBDatabase;
        bWriteUIBTransaction.Options := [tpConsistency, tpWrite, tpWait];
        Try
          bUIBDatabase.Connected := TRUE;
        Except On E: Exception Do
          //Add Here some log or exception
          //SafeLog(E.Message);
        End;
        {$ENDIF};
        {$IFDEF IBX}
        bIBXDatabase := TIBDataBase.Create(nil);
        bIBXDatabase.DatabaseName := Format('%s:%s', [aServer, aBase]);
        bIBXDatabase.Params.Add(Format('user_name=%s', [aUser]));
        bIBXDatabase.Params.Add(Format('password=%s', [aPassword]));
        bIBXDatabase.Params.Add('lc_ctype=UTF-8');
        bIBXDatabase.LoginPrompt := FALSE;

        bReadIBXTransaction := TIBTransaction.Create(nil);
        bReadIBXTransaction.DefaultDatabase := bIBXDatabase;
        bReadIBXTransaction.DefaultAction := TACommit;
        bReadIBXTransaction.Params.Add('read_committed');
        bReadIBXTransaction.Params.Add('rec_version');
        bReadIBXTransaction.Params.Add('nowait');

        bWriteIBXTransaction := TIBTransaction.Create(nil);
        bWriteIBXTransaction.DefaultDatabase := bIBXDatabase;
        bWriteIBXTransaction.DefaultAction := TARollback;
        bWriteIBXTransaction.Params.Add('write');
        bWriteIBXTransaction.Params.Add('consistency');

        Try
          bIBXDatabase.Connected := TRUE;
        Except On E: Exception Do
          Raise E;
          //SafeLog(E.Message);
        End;
        {$ENDIF}
      End;
    DB_MS:
      Begin
        {$IFDEF TDS}
        bTDSDatabase := TTDSCTDataBase.Create(nil);
        bTDSDatabase.ServerVersion := svMSSQL2008;
        bTDSDatabase.ServerName := aServer;
        bTDSDatabase.Database := aBase;
        bTDSDatabase.UserName := aUser;
        bTDSDatabase.Password := aPassword;
        bTDSDatabase.LibraryName := aLib;

        bReadTDSTransaction := TTDSCTTransaction.Create(nil);
        bReadTDSTransaction.DataBase := bTDSDatabase;

        bWriteTDSTransaction := TTDSCTTransaction.Create(nil);
        bWriteTDSTransaction.DataBase := bTDSDatabase;

        Try
          bTDSDatabase.Connected := TRUE;
        Except On E: Exception Do
          SafeLog(E.Message);
        End;
        {$ENDIF};
      End;
  End;
End;

Destructor BConnectionClass.Burn;
Begin
  Case bKind Of
    DB_FB:
      Begin
        {$IFDEF UIB}
        If bReadUIBTransaction.InTransaction Then bReadUIBTransaction.Commit;
        If bWriteUIBTransaction.InTransaction Then
          bWriteUIBTransaction.Rollback;
        bReadUIBTransaction.Free;
        bWriteUIBTransaction.Free;
        bUIBDatabase.Connected := FALSE;
        bUIBDatabase.Free;
        {$ENDIF};
        {$IFDEF IBX}
        If bReadIBXTransaction.InTransaction Then bReadIBXTransaction.Commit;
        If bWriteIBXTransaction.InTransaction THen
          bWriteIBXTransaction.Rollback;
        bReadIBXTransaction.Free;
        bWriteIBXTransaction.Free;
        bIBXDatabase.Free;
        {$ENDIF}
      End;
    DB_MS:
      Begin
        {$IFDEF TDS}
        If bReadTDSTransaction.Active Then bReadTDSTransaction.Commit;
        If bWriteTDSTransaction.Active Then bWriteTDSTransaction.Rollback;
        bReadTDSTransaction.Free;
        bWriteTDSTransaction.Free;
        bTDSDatabase.Free;
        {$ENDIF};
      End;
  End;
End;

{ BConnectionPoolClass }

{$IFDEF UIB}
Procedure BConnectionsPoolClass.HoldUIBConnection(Out aDatabase: TUIBDataBase;
  Out aRead, aWrite: TUIBTransaction);
Var
  i, aStep: Integer;
  aWasFound: Boolean;
  aConnection: BConnectionClass;
Begin
  aStep := 0;
  aWasFound := FALSE;
  With bPool.LockList Do
    Repeat
      For i := 0 To Count - 1 Do
        Begin
          aConnection := BConnectionClass(Items[i]);
          If Not(aConnection.Busy) Then
            Begin
              aConnection.HoldUIBConnection(aDatabase, aRead, aWrite);
              aWasFound := TRUE;
              Break;
            End;
          Inc(aStep);
          Sleep(20);
        End;
    Until aWasFound Or (aStep < STEP_MAX);
  bPool.UnlockList;
End;

Procedure BConnectionsPoolClass.FreeUIBConnection(
  Const aDatabase: TUIBDataBase);
Var
  i: Integer;
Begin
  With bPool.LockList Do
    For i := 0 To Count - 1 Do
      If BConnectionClass(Items[i]).UIBDatabase = aDatabase Then
        Begin
          BConnectionClass(Items[i]).FreeUIBConnection;
          Break;
        End;
  bPool.UnlockList;
End;
{$ENDIF}

{$IFDEF TDS}
Procedure BConnectionsPoolClass.HoldTDSConnection(Out aDatabase: TTDSCTDataBase;
  Out aRead, aWrite: TTDSCTTransaction);
Var
  i, aStep: Integer;
  aWasFound: Boolean;
  aConnection: BConnectionClass;
Begin
  aWasFound := FALSE;
  aStep := 0;
  With bPool.LockList Do
    Repeat
      For i := 0 To Count - 1 Do
        Begin
          aConnection := BConnectionClass(Items[i]);
          If Not(aConnection.Busy) Then
            Begin
              aConnection.HoldTDSConnection(aDatabase, aRead, aWrite);
              aWasFound := TRUE;
              Break;
            End;
          Inc(aStep);
          Sleep(20);
        End;
    Until aWasFound Or (aStep < STEP_MAX);
  bPool.UnlockList;
End;

Procedure BConnectionsPoolClass.FreeTDSConnection(
  Const aDatabase: TTDSCTDataBase);
Var
  i: Integer;
Begin
  With bPool.LockList Do
    For i := 0 To Count - 1 Do
      If BConnectionClass(Items[i]).TDSDatabase = aDatabase Then
        Begin
          BConnectionClass(Items[i]).FreeTDSConnection;
          Break;
        End;
  bPool.UnlockList;
End;
{$ENDIF}

{$IFDEF IBX}
Procedure BConnectionsPoolClass.HoldIBXConnection(Out aDatabase: TIBDataBase;
  Out aRead, aWrite: TIBTransaction);
Var
  i, aStep: Integer;
  aWasFound: Boolean;
  aConnection: BConnectionClass;
Begin
  aWasFound := FALSE;
  aStep := 0;
  With bPool.LockList Do
    Repeat
      For i := 0 To Count - 1 Do
        Begin
          aConnection := BConnectionClass(Items[i]);
          If Not(aConnection.Busy) Then
            Begin
              aConnection.HoldIBXConnection(aDatabase, aRead, aWrite);
              aWasFound := TRUE;
              Break;
            End;
          Inc(aStep);
          Sleep(20);
        End;
    Until aWasFound Or (aStep < STEP_MAX);
  If Not(aWasFound) Then Raise Exception.Create('No one free connection');
  bPool.UnlockList;
End;

Procedure BConnectionsPoolClass.FreeIBXConnection(
  Const aDatabase: TIBDataBase);
Var
  i: Integer;
Begin
  With bPool.LockList Do
    For i := 0 To Count - 1 Do
      If BConnectionClass(Items[i]).IBXDatabase = aDatabase Then
        Begin
          BConnectionClass(Items[i]).FreeIBXConnection;
          Break;
        End;
  bPool.UnlockList;
End;
{$ENDIF}

Procedure BConnectionsPoolClass.Connect(Const aPoolDepth: Integer);
Var
  i: Integer;
  aConnection: BConnectionClass;
Begin
  If bConnected Then
    Begin
      Raise Exception.Create('Already Connected');
      Exit;
    End;
  If Not(PoolDepth = 0) Then
    ;//Add Here some log or exception
    //SafeLog('Pool not empty on connect');
  bPoolDepth := 0;
  For i := 1 To aPoolDepth Do
    Begin
      aConnection := BConnectionClass.Build(Kind,Server,Base,User,Password,Lib);
      If aConnection.Connected Then
        Begin
          bPool.Add(aConnection);
          Inc(bPoolDepth);
        End;
    End;
  If Not(aPoolDepth=PoolDepth) Then
    ;//Add Here some log or exception
    //SafeLog('Not all connections established');
  bConnected := TRUE;
End;

Procedure BConnectionsPoolClass.Disconnect;
Var
  i: Integer;
Begin
  With bPool.LockList Do
    Begin
      For i := 0 To Count - 1 Do
        BConnectionClass(Items[i]).Burn;
      bPoolDepth := 0;
      bConnected := FALSE;
      Clear;
    End;
  bPool.UnlockList
End;

Constructor BConnectionsPoolClass.Build(Const aKind: Integer; Const aServer, aBase,
  aUser, aPassword, aLib: String);
Begin
  bKind := aKind;
  bServer := aServer;
  bBase := aBase;
  bUser := aUser;
  bPassword := aPassword;
  bLib := aLib;
  bPool := TThreadList.Create;
End;

Destructor BConnectionsPoolClass.Burn;
Var
  i: Integer;
Begin
  With bPool.LockList Do
    For i := 0 To Count - 1 Do
      BConnectionClass(Items[i]).Burn;
  bPool.UnlockList;
  bPool.Free;
End;

{  BQueryClass  }

Constructor BQueryClass.Build(Const aID: Integer);
Begin
  {$IFDEF UIB}
    bUIBQuery := TUIBQuery.Create(nil);
  {$ENDIF}
  {$IFDEF TDS}
    bTDSQuery := TTDSCTQuery.Create(nil);
  {$ENDIF}
  {$IFDEF IBX}
    bIBXQuery := TIBSQL.Create(nil);
  {$ENDIF}
  bID := aID;
  bKind := DBManager.GetKind(aID);
  Case bKind Of
    DB_FB:
      Begin
      {$IFDEF UIB}
        DBManager.HoldUIBConnection(aID, bUIBDatabase, bUIBRead,
          bUIBWrite);
        bUIBQuery.DataBase := bUIBDatabase;
      {$ENDIF}
      {$IFDEF IBX}
        DBManager.HoldIBXConnection(aID, bIBXDatabase, bIBXRead, bIBXWrite);
        bIBXQuery.Database := bIBXDatabase;
      {$ENDIF}
      End;

    DB_MS:
      Begin
      {$IFDEF TDS}
        DBManager.HoldTDSConnection(aID, bTDSDatabase, bTDSRead,
          bTDSWrite);
        bTDSQuery.DataBase := bTDSDatabase;
      {$ENDIF}
      End;
  End;
End;

Destructor BQueryClass.Burn;
Begin
  Case bKind Of
    DB_FB:
      Begin
        {$IFDEF UIB}
        DBManager.FreeUIBConnection(bID, bUIBQuery.DataBase);
        bUIBQuery.Free;
        {$ENDIF}
        {$IFDEF IBX}
        DBManager.FreeIBXConnection(bID, bIBXQuery.Database);
        bIBXQuery.Free;
        {$ENDIF}
      End;
    DB_MS:
      Begin
        {$IFDEF TDS}
        DBManager.FreeTDSConnection(bID, bTDSQuery.DataBase);
        bTDSQuery.Free;
        {$ENDIF}
      End;
  End;
End;

{$IFDEF UIB}
Function BQueryClass.GetUIB(Const aSQL: BSQLClass): Boolean;
Var
  i: Integer;
Begin
  Result := FALSE;
  With bUIBQuery Do
    Begin
      Transaction := bUIBRead;
      If Not(Transaction.InTransaction) Then Transaction.StartTransaction;
      Params.Clear;
      SQL.Clear;
      SQL.Add(aSQL.SQL);
      For i := 0 To aSQL.Params.Count - 1 Do
        Params.ByNameAsString[BSQLParamClass(aSQL.Params[i]).Iterator] :=
          BSQLParamClass(aSQL.Params[i]).Value;
      Try
        If aSQL.IsProcedure Then Execute
        Else Open;
      Except On E: Exception Do
        Begin
          ;//Add Here some log or exception
          //Log(SQL.Text);
          //Log(E.Message);
        End;
      End;
    End;
  Result := TRUE;
End;

Function BQueryClass.PostUIB(Const aSQL: BSQLClass): Boolean;
Var
  i: Integer;
  aMessage: String;
  aParam: BSQLParamClass;
Begin
  Result := FALSE;
  With bUIBQuery Do
    Begin
      Transaction := bUIBWrite;
      If Not(Transaction.InTransaction) Then Transaction.StartTransaction;
      Try
        Params.Clear;
        SQL.Text := aSQL.SQL;
        For i := 0 To aSQL.Params.Count - 1 Do
          Begin
            aParam := BSQLParamClass(aSQL.Params[i]);
            If aParam.Stream = nil Then
              Params.ByNameAsString[aParam.Iterator] :=  aParam.Value
            Else
              ParamsSetBlob(aParam.Iterator, aParam.Stream);
          End;
        If aSQL.IsProcedure Then Execute
        Else ExecSQL;
      Except On E: Exception Do
        Begin
          Log(SQL.Text);
          Log(E.Message);
          aMessage := E.Message;
          aMessage += #10'Message: ' + SQL.Text;
          Transaction.Rollback;
        End;
      End;
      If Not(aMessage = '') Then
        Raise BExceptionClass.Build(22, aMessage);
    End;
  Result := TRUE;
End;
{$ENDIF}

{$IFDEF TDS}
Function BQueryClass.GetTDS(Const aSQL: BSQLClass): Boolean;
Var
  i: Integer;
Begin
  Result := FALSE;
  With bTDSQuery Do
    Begin
      Transaction := bTDSRead;
      Params.Clear;
      SQL.Text := aSQL.SQL;
      For i := 0 To aSQL.Params.Count - 1 Do
        Params.ParamByName(BSQLParamClass(aSQL.Params[i]).Iterator).AsString :=
          BSQLParamClass(aSQL.Params[i]).Value;
      Try
        If aSQL.IsProcedure Then ExecSQL
        Else Open;
      Except On E: Exception Do
        Begin
          DirectLog(SQL.Text);
          Log(E.Message);
        End;
      End;
    End;
  Result := TRUE;
End;

Function BQueryClass.PostTDS(Const aSQL: BSQLClass): Boolean;
Var
  i: Integer;
  aParam: BSQLParamClass;
Begin
  Result := FALSE;
  With bTDSQuery Do
    Begin
      Transaction := bTDSWrite;
      If Not(Transaction.Active) Then Transaction.StartTransaction;
      Try
        Params.Clear;
        SQL.Text := aSQL.SQL;
        For i := 0 To aSQL.Params.Count - 1 Do
          Begin
            aParam := BSQLParamClass(aSQL.Params[i]);
            // TODO: no blob support
            If aParam.Stream = nil Then
              ParamByName(aParam.Iterator).Value :=  aParam.Value;
          End;
        ExecSQL;
      Except On E: Exception Do
        Begin
          Log(SQL.Text);
          Log(E.Message);
          Transaction.Rollback;
          Exit;
        End;
      End;
    End;
  Result := TRUE;
End;
{$ENDIF}

{$IFDEF IBX}
Function BQueryClass.GetIBX(Const aSQL: BSQLClass): Boolean;
Var
  i: Integer;
Begin
  Result := FALSE;
  With bIBXQuery Do
    Begin
      If Open Then Close;
      Database := bIBXDatabase;
      Transaction := bIBXRead;
      If Not(Transaction.InTransaction) Then Transaction.StartTransaction;
      SQL.Text := aSQL.SQL;
      For i := 0 To aSQL.Params.Count - 1 Do
        Params.ByName(BSQLParamClass(aSQL.Params[i]).Iterator).AsString :=
          BSQLParamClass(aSQL.Params[i]).Value;
      Try
        ExecQuery;
      Except On E: Exception Do
        Begin
          Raise E;
          //SafeLog(SQL.Text);
          //SafeLog(E.Message);
        End;
      End;
    End;
  Result := TRUE;
End;

Function BQueryClass.PostIBX(Const aSQL: BSQLClass): Boolean;
Var
  i: Integer;
  aParam: BSQLParamClass;
Begin
  Result := FALSE;
  With bIBXQuery Do
    Begin
      Transaction := bIBXWrite;
      If Not(Transaction.InTransaction) Then Transaction.StartTransaction;
      Try
        SQL.Text := aSQL.SQL;
        For i := 0 To aSQL.Params.Count - 1 Do
          Begin
            aParam := BSQLParamClass(aSQL.Params[i]);
            // TODO: no blob support
            If aParam.Stream = nil Then
              ParamByName(aParam.Iterator).Value :=  aParam.Value;
          End;
        If Open Then Close;
        ExecQuery;
      Except On E: Exception Do
        Begin
          //SafeLog(SQL.Text);
          //SafeLog(E.Message);
          Transaction.Rollback;
          Raise E;
          Exit;
        End;
      End;
    End;
  Result := TRUE;
End;
{$ENDIF}

Function BQueryClass.ByString(Const aString: String): String;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsString[aString]{$ENDIF}
      {$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsString{$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := bTDSQuery.ByNameAsString[aString]{$ENDIF};
  End;
End;

Function BQueryClass.ByDouble(Const aString: String): Double;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsDouble[aString]{$ENDIF}
      {$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsFloat{$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := bTDSQuery.ByNameAsFloat[aString]{$ENDIF};
  End;
End;

Function BQueryClass.ByInteger(Const aString: String): Integer;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsInteger[aString]{$ENDIF}
      {$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsInteger{$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := bTDSQuery.ByNameAsInteger[aString]{$ENDIF};
  End;
End;

Function BQueryClass.ByInt64(Const aString: String): Int64;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsInt64[aString]{$ENDIF}
      {$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsInt64{$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := bTDSQuery.ByNameAsInt64[aString]{$ENDIF};
  End;
End;

Function BQueryClass.ByBoolean(Const aString: String): Boolean;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result:=bUIBQuery.Fields.ByNameAsBoolean[aString]{$ENDIF}
      {$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsInteger=1{$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := bTDSQuery.ByNameAsInteger[aString] = 1{$ENDIF};
  End;
End;

Function BQueryClass.ByDate(Const aString: String): TDateTime;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsDate[aString]{$ENDIF}
      {$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsDateTime{$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := bTDSQuery.ByNameAsDate[aString]{$ENDIF};
  End;
End;

Function BQueryClass.ByDateTime(Const aString: String): TDateTime;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result := bUIBQuery.Fields.ByNameAsDateTime[aString]{$ENDIF}
      {$IFDEF IBX}Result := bIBXQuery.FieldByName(aString).AsDateTime{$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := bTDSQuery.ByNameAsDateTime[aString]{$ENDIF};
  End;
End;

Function BQueryClass.Get(Const aSQL: BSQLClass): Boolean;
Begin
  Result := FALSE;
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result := GetUIB(aSQL){$ENDIF}
      {$IFDEF IBX}Result := GetIBX(aSQL){$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := GetTDS(aSQL){$ENDIF};

  End;
End;

Function BQueryClass.Get(Const aString: String;
  Const aForExecute: Boolean = FALSE): Boolean;
Var
  aSQL: BSQLClass;
Begin
  aSQL := BSQLClass.Build(aString, aForExecute);
  Result := Get(aSQL);
  aSQL.Burn;
End;

Function BQueryClass.Get(Const aMask: String; Const aParams: Array Of Const;
  Const aForExecute: Boolean): Boolean;
Begin
  Get(Format(aMask, aParams), aForExecute);
End;

Function BQueryClass.Post(Const aSQL: BSQLClass): Boolean;
Begin
  Result := FALSE;
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result := PostUIB(aSQL){$ENDIF}
      {$IFDEF IBX}Result := PostIBX(aSQL){$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := PostTDS(aSQL){$ENDIF};
  End;
End;

Function BQueryClass.Post(Const aString: String;
  Const aForExecute: Boolean): Boolean;
Var
  aSQL: BSQLClass;
Begin
  aSQL := BSQLClass.Build(aString, aForExecute);
  Result := Post(aSQL);
  aSQL.Burn;
End;

Function BQueryClass.Post(Const aMask: String;
  Const aParams: Array Of Const; Const aForExecute: Boolean): Boolean;
Begin
  Result := Post(Format(aMask, aParams), aForExecute);
End;

Function BQueryClass.EOF: Boolean;
Begin
  Result := TRUE;
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}Result := bUIBQuery.Eof{$ENDIF}
      {$IFDEF IBX}Result := bIBXQuery.EOF{$ENDIF};
    DB_MS:
      {$IFDEF TDS}Result := bTDSQuery.Eof{$ENDIF};
  End;
End;

Procedure BQueryClass.Go;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}
        With bUIBQuery Do
          Begin
            Transaction := bUIBWrite;
            If Transaction.InTransaction Then Transaction.Commit;
          End{$ENDIF}
      {$IFDEF IBX}
        With bIBXQuery Do
          Begin
            Transaction := bIBXWrite;
            If Transaction.InTransaction Then Transaction.Commit;
          End{$ENDIF};
    DB_MS:
      {$IFDEF TDS}
        With bTDSQuery Do
          Begin
            Transaction := bTDSWrite;
            If Transaction.Active Then Transaction.Commit;
          End{$ENDIF};

  End;
End;

Procedure BQueryClass.Next;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}bUIBQuery.Next{$ENDIF}
      {$IFDEF IBX}bIBXQuery.Next{$ENDIF};
    DB_MS:
      {$IFDEF TDS}bTDSQuery.Next{$ENDIF};
  End;
End;

Procedure BQueryClass.First;
Begin
  Case bKind Of
    DB_FB:
      {$IFDEF UIB}bUIBQuery.First{$ENDIF}
      {$IFDEF IBX}Raise Exception.Create('First is not implemented'){$ENDIF};
    DB_MS:
      {$IFDEF TDS}Raise Exception.Create('First is not implemented'){$ENDIF};
  End;
End;

{ BDBManagerClass }

Constructor BDBManagerClass.Build;
Begin
  bPools := TThreadList.Create;
  DefaultFormatSettings.LongDateFormat := 'dd.mm.yyyy';
  DefaultFormatSettings.ShortDateFormat := 'dd.mm.yyyy';
End;

Destructor BDBManagerClass.Burn;
Var
  i: Integer;
Begin
  With bPools.LockList Do
    For i := 0 To Count - 1 Do
      BConnectionsPoolClass(Items[i]).Burn;
  bPools.UnlockList;
  bPools.Free;
End;

Function BDBManagerClass.AddDatabase(Const aKind: Integer;
  Const aServer, aBase, aUser, aPassword: String; Const aLib: String): Integer;
Begin
  With bPools.LockList Do
    Begin
      Result := Count;
      Add(BConnectionsPoolClass.Build(aKind,aServer,aBase,aUser,aPassword,aLib))
    End;
  bPools.UnlockList;
End;

Procedure BDBManagerClass.RemoveDatabase(Const aIndex: Integer);
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  With bPools.LockList Do
    Begin
      If (Count = 0) Or Not(aIndex < Count) Then
        Raise Exception.Create('Illegal Index on Remove Database');
      aConnectionPool := BConnectionsPoolClass(Items[aIndex]);
      aConnectionPool.Burn;
      Delete(aIndex);
    End;
  bPools.UnlockList;
End;

Function BDBManagerClass.GetKind(Const aIndex: Integer): Integer;
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
  bPools.UnlockList;
  Result := aConnectionPool.Kind;
End;

Procedure BDBManagerClass.Connect(Const aIndex: Integer;
  Const aPoolSize: Integer);
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
  bPools.UnlockList;
  aConnectionPool.Connect(aPoolSize);
End;

{$IFDEF IBX}
Procedure BDBManagerClass.HoldIBXConnection(Const aIndex: Integer;
  Out aDatabase: TIBDataBase; Out aRead, aWrite: TIBTransaction);
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
  bPools.UnlockList;
  aConnectionPool.HoldIBXConnection(aDatabase, aRead, aWrite);
End;

Procedure BDBManagerClass.FreeIBXConnection(Const aIndex: Integer;
  Const aDatabase: TIBDataBase);
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
  bPools.UnlockList;
  aConnectionPool.FreeIBXConnection(aDatabase);
End;
{$ENDIF}

{$IFDEF UIB}
Procedure BDBManagerClass.HoldUIBConnection(Const aIndex: Integer;
  Out aDatabase: TUIBDataBase; Out aRead, aWrite: TUIBTransaction);
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
  bPools.UnlockList;
  aConnectionPool.HoldUIBConnection(aDatabase, aRead, aWrite);
End;

Procedure BDBManagerClass.FreeUIBConnection(Const aIndex: Integer;
  Const aDatabase: TUIBDataBase);
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
  bPools.UnlockList;
  aConnectionPool.FreeUIBConnection(aDatabase);
End;

{$ENDIF}

{$IFDEF TDS}
Procedure BDBManagerClass.HoldTDSConnection(Const aIndex: Integer;
  Out aDatabase: TTDSCTDataBase; Out aRead, aWrite: TTDSCTTransaction);
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
  bPools.UnlockList;
  aConnectionPool.HoldTDSConnection(aDatabase, aRead, aWrite);
End;

Procedure BDBManagerClass.FreeTDSConnection(Const aIndex: Integer;
  Const aDatabase: TTDSCTDataBase);
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
  bPools.UnlockList;
  aConnectionPool.FreeTDSConnection(aDatabase);
End;
{$ENDIF}

Procedure BDBManagerClass.Disconnect(Const aIndex: Integer);
Var
  aConnectionPool: BConnectionsPoolClass;
Begin
  aConnectionPool := BConnectionsPoolClass(bPools.LockList.Items[aIndex]);
  bPools.UnlockList;
  aConnectionPool.Disconnect;
End;

Initialization
Begin
  If DBManager = nil Then DBManager := BDBManagerClass.Build;
End;

Finalization
Begin
  If Not(DBManager = nil) And (DBManager.UnitName = 'BQueryUnit') Then
    DBManager.Burn;
End;

End.
